package com.mango.leet.code.middle;

/**
 * 1498. 满足条件的子序列数目
 */

import java.math.BigInteger;
import java.util.*;

/**
 * 给你一个整数数组 nums 和一个整数 target 。
 *
 * 请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。
 *
 * 由于答案可能很大，请将结果对 10^9 + 7 取余后返回。
 *
 *  
 *
 * 示例 1：
 *
 * 输入：nums = [3,5,6,7], target = 9
 * 输出：4
 * 解释：有 4 个子序列满足该条件。
 * [3] -> 最小元素 + 最大元素 <= target (3 + 3 <= 9)
 * [3,5] -> (3 + 5 <= 9)
 * [3,5,6] -> (3 + 6 <= 9)
 * [3,6] -> (3 + 6 <= 9)
 * 示例 2：
 *
 * 输入：nums = [3,3,6,8], target = 10
 * 输出：6
 * 解释：有 6 个子序列满足该条件。（nums 中可以有重复数字）
 * [3] , [3] , [3,3], [3,6] , [3,6] , [3,3,6]
 * 示例 3：
 *
 * 输入：nums = [2,3,3,4,6,7], target = 12
 * 输出：61
 * 解释：共有 63 个非空子序列，其中 2 个不满足条件（[6,7], [7]）
 * 有效序列总数为（63 - 2 = 61）
 * 示例 4：
 *
 * 输入：nums = [5,2,4,1,7,6,8], target = 16
 * 输出：127
 * 解释：所有非空子序列都满足条件 (2^7 - 1) = 127
 *  
 *
 * 提示：
 *
 * 1 <= nums.length <= 10^5
 * 1 <= nums[i] <= 10^6
 * 1 <= target <= 10^6
 *
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/number-of-subsequences-that-satisfy-the-given-sum-condition
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 */
public class LC1498 {
    public static void main(String[] args) {
        int[] nums = {3,3,6,8};
        System.out.println(new Solution().numSubseq(nums,10));

    }
    static class Solution {
        public int numSubseq(int[] nums, int target) {
            // 先排序
            Arrays.sort(nums);
            // 如果数组最小元素都比target大，则直接返回结果0
            if(nums[0] > target){
                return 0;
            }
            // 利用BigInteger防止溢出
            BigInteger res = BigInteger.valueOf(0);
            for (int i = 0; i < nums.length; i++) {
                // 从i开始找target - nums[i] 的值下标
                int index = get(nums, i, target - nums[i]);
                if(index == i){
                    break;
                }else{
                    // 利用公式2^(index - i - 1) 次方计算符合的子序列
                    res = res.add(BigInteger.valueOf(2).pow(index - i - 1));
                }
            }
            return res.mod(BigInteger.valueOf(10).pow(9).add(BigInteger.valueOf(7))).intValue() ;
        }
        // 2分查找定位target在数组内的坐标
        public int get(int[] nums, int start, int target){
            int l = start;
            int r = nums.length - 1;
            if(l == r){
                return nums[l] > target ? l : nums.length;
            }
            while(l != r){
                int m = l + (r - l) / 2;
                if(nums[m] > target){
                    r = m;
                }else{
                    l = m + 1;
                }
            }
            return  nums[l] > target ? l : nums.length;
        }
        /**
         * 滑动窗口和双端队列，均无法处理重复的问题。因为这题不需要保证序列的顺序性。（思路错误）
         */
        public int numSubseq3(int[] nums, int target) {
            // 滑动窗口
            int len = 0;
            int size = 0;
            while (len <= nums.length){
                for(int i=0;i<nums.length;i++){
                    if(i+len<nums.length-1 && nums[i] + nums[i+len] <= target ){
                        size++;
                        System.out.println("len:" + len + " accept:" + i + "-" + (i+len));
                    }
                }
                len++;
            }
            return size;
        }
        public int numSubseq2(int[] nums, int target) {
            // 双端队列
            int size = 0;
            for(int len=1;len<=nums.length;len++){
                Deque<Integer> deque = new LinkedList<>();
                for(int i=0;i<nums.length;i++){
                    deque.addLast(nums[i]);
                    //System.out.println("now:"+Arrays.toString(new ArrayList(deque).toArray()));
                    if (deque.size() == len) {
                        if (deque.peekLast() + deque.peekFirst() <= target) {
                            size++;
                            System.out.println("len:" + len + " accept:" + Arrays.toString(new ArrayList(deque).toArray()));
                        }
                        deque.pollLast();
                    }
                }
            }
            return size;
        }
    }
}
